<?php
if (!class_exists("Redis"))
{
    return;
}
class NamespacedRedis extends Redis
{
    public function __construct(Redis $redis, $namespace)
    {
        $this->core = $redis;
        $this->namespace = $namespace;
    }

    public function __call($method, $arg)
    {
        $arg[0] = $this->namespace.$arg[0];
        $arg_str = implode(",", $arg);
        eval("\$rt = \$this->core->\$method(\$arg_str);");

        return $rt;
    }

}//end class


class ReplicaRedis extends Redis 
{
    const REPLICA_ADDED_EXPIRE = 60; 

    private $replicas = array();
    private $expiry;

    private static $getFuncNames = array("get", "lrange", "zrange");
    
    public function __construct(Redis $core, $replicas = NULL)
    { 
        $replicas = ( $replicas && is_numeric( $replicas) && $replicas > 1) ? intval($replicas) : 3;

        for( $i = 0; $i < $replicas; $i++)
        {
            $this->replicas[] = new NamespacedMemcache($core, '_replica_' . $replicas . '_' . $i . '.');
        }

        shuffle($this->replicas);
        $this->expiry = new NamespacedRedis($core, '_replica_exp_' . $replicas);
    }

    public function __call($method, $arg)
    {
        if (in_array(self::$getFuncNames, $method))
        {
            return $this->__fetch($method, $arg);
        }
        else
        {
            return $this->__store($method, $arg);
        }
    }
    
    private function __fetch($method, $arg)
    {
        $key = $arg[0];

        if(!$key) 
        {
            return false;
        }

        $match = false;

        if(!$this->expiry instanceof Redis) 
        {
            $e = new Exception('invalid-expiry-obj');
            syslog(LOG_INFO, 'PHP ' . $e->__toString());
            return false;
        }
        
        //$unexpired = $this->expiry->get($keys);
        //get from expiry
        eval("\$unexpired = \$this->expiry->\$method->get(\$key)");
        
        $expired = NULL;

        //calulate expired 
        if (false === $unexpired) 
        {
            $expired = true;
        }
        
        //get from replica
        foreach($this->replicas as $replica)
        {
            $res = $replica->get($key);

            if($res === false) 
            {
                continue;
            }
            else
            {
                $match = $v;
                break;
            }
        }
        
        //if expired in expiry, and get from replia, set to expiry
        if($expired && $match)
        {
            //set to expiry
            $bool = $this->expiry->add($key, 1, 0, 60);

            if (!$bool)
            {
                $match = false;
            }
        }

        return $match;
    }
    
    private function __store($k, $v, $flag = NULL, $expire = 1000)
    {
        $res = false;

        $this->expiry->set($k, 1, 0, $expire);//$k, $v, compress, expire

        foreach($this->replicas as $replica)
        {
            $r = $replica->set($k, $v, $flag, $expire + self::REPLICA_ADDED_EXPIRE);
            if($r) 
            {
                $res = $r;
            }
        }

        return $res;
    }
    
}//end class
